package com.bb.flow.service.imp.executor.node;

import com.bb.flow.api.FlowEndCallback;
import com.bb.flow.api.entries.NotifyFlowEndInfo;
import com.bb.flow.api.exception.ParseException;
import com.bb.flow.api.type.ExecuteState;
import com.bb.flow.api.type.FlowEndType;
import com.bb.flow.config.AbstractConfigNode;
import com.bb.flow.config.FlowInfo;
import com.bb.flow.config.enumtype.NodeType;
import com.bb.flow.config.enumtype.WaitType;
import com.bb.flow.config.node.SubFlowConfigNode;
import com.bb.flow.service.FlowDriver;
import com.bb.flow.service.api.NodeStateCallback;
import com.bb.flow.service.exception.FlowTemplateNameNullException;
import com.bb.flow.service.exception.FlowTemplateNotFoundException;
import com.bb.flow.service.imp.AbstractNodeExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @author bigbeard
 * 子流程执行器
 */
public class SubFlowNodeExecutor extends AbstractNodeExecutor {

    private final static Logger logger = LoggerFactory.getLogger(SubFlowNodeExecutor.class);

    private FlowDriver flowDriver;

    /**
     * 设置父流程
     *
     * @param flowDriver 流程
     */
    public void setFlowDriver(FlowDriver flowDriver) {
        this.flowDriver = flowDriver;
    }

    @Override
    protected String getThreadName() {
        return "execute-subflow-thread-%s";
    }

    @Override
    protected void executeNode(
            AbstractConfigNode node, FlowInfo flowInfo
            , NodeStateCallback nodeStateCallback) {
        if (canExecute(node)) {
            return;
        }
        this.setNodeStateCallback(nodeStateCallback);
        this.setCurrentFlowInfo(flowInfo);
        this.setCurrentNode(node);
        flowInfo.getFlowContext().setNodeKey(node.getNodeKey());
        notifyExecuteState(ExecuteState.EXECUTING);
        SubFlowConfigNode subFlowConfigNode = (SubFlowConfigNode) node;
        if (node.getWaitType() == WaitType.WAIT_TIME && node.getWaitTime() > 0) {
            flowDriver.registerFlowEndCallback(this.flowEndCallback);
            Future<?> execute = execute(flowDriver, subFlowConfigNode.getSubFlowTemplateName(), flowInfo
                    , flowInfo.getFlowContext());
            try {
                execute.get(subFlowConfigNode.getWaitTime(), TimeUnit.SECONDS);
            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                flowDriver.unregisterFlowEndCallback(this.flowEndCallback);
                notifyExecuteState(ExecuteState.TIME_OUT);
                logger.error("流程模版[{}]中节点[{}]子流程[{}]执行超时", flowInfo.getFlowTemplateName(), node.getNodeKey(), subFlowConfigNode.getSubFlowTemplateName());
            }
        } else if (node.getWaitType() == WaitType.NOT_WAIT) {
            execute(flowDriver, subFlowConfigNode.getSubFlowTemplateName(), flowInfo, flowInfo.getFlowContext());
            notifyExecuteState(ExecuteState.FINISH);
        } else {
            flowDriver.registerFlowEndCallback(this.flowEndCallback);
            execute(flowDriver, subFlowConfigNode.getSubFlowTemplateName(), flowInfo, flowInfo.getFlowContext());
        }
    }

    /**
     * 节点类型
     *
     * @return 节点类型
     */
    @Override
    protected NodeType getAllowExecuteNodeType() {
        return NodeType.SUBFLOW_NODE;
    }

    /**
     * 执行当前节点操作
     *
     * @param executeSubFlow   驱动
     * @param flowTemplateName 流程模版名称
     * @return 任务执行future
     */
    private Future<?> execute(FlowDriver executeSubFlow, String flowTemplateName, FlowInfo parentFlow, Object context) {
        return executorService.submit(() -> {
            logger.debug("FunctionNodeExecutor:node executing start");
            if (parentFlow == null) {
                logger.error("流程模版[{}]父流程模版为null,参数错误", flowTemplateName);
                notifyExecuteState(ExecuteState.EXCEPTION);
                return;
            }
            try {
                String instanceName = String.format("%s_%s_SubFlow_instance", getCurrentNode().getNodeKey(), flowTemplateName);
                FlowInfo f = parentFlow;
                StringBuilder sb = new StringBuilder();
                sb.append(instanceName);
                while (f != null) {
                    sb.insert(0, "-").insert(0, f.getFlowTemplateName());
                    f = f.getParentFlow();
                }
                executeSubFlow.startFlow(flowTemplateName, sb.toString(), parentFlow, context);
            } catch (FlowTemplateNotFoundException | FlowTemplateNameNullException | ParseException ex) {
                logger.error("流程模版[{}]的子流程节点[{}]在创建子流程是发生异常,异常:{}", parentFlow.getFlowTemplateName(), getCurrentNode().getNodeKey(), ex.toString());
                notifyExecuteState(ExecuteState.EXCEPTION);
            }
            logger.debug("FunctionNodeExecutor:node executing end");
        });
    }

    private void dealFlowEnd(NotifyFlowEndInfo notifyFlowEndInfo) {
        SubFlowConfigNode currentNode = (SubFlowConfigNode) getCurrentNode();
        if (!currentNode.getSubFlowTemplateName().equals(notifyFlowEndInfo.getFlowTemplateName())) {
            logger.debug("流程模版[{}]不是当前的子流程,不执行,当前子流程[{}]", notifyFlowEndInfo.getFlowTemplateName(),
                    currentNode.getSubFlowTemplateName());
            return;
        }
        flowDriver.unregisterFlowEndCallback(flowEndCallback);
        ExecuteState executeState = ExecuteState.FINISH;
        if (notifyFlowEndInfo.getFlowEndType() == FlowEndType.EXCEPTION_END
                || notifyFlowEndInfo.getFlowEndType() == FlowEndType.NOT_EXECUTE) {
            executeState = ExecuteState.NOT_EXECUTE;
        }
        notifyExecuteState(executeState);
    }

    private final FlowEndCallback flowEndCallback = this::dealFlowEnd;


}
